#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "structures.h"
#include "mod_file.h"
#include "mod_network.h"
#include "mod_protocol.h"
#include "mod_config.h"
#include "mod_balance.h"
#include <glib-2.0/glib.h>

extern GList * listaSP;
extern GList * connectedPeer;
extern GStaticRWLock fileRepresentationLock;
extern GStaticRWLock connectedPeerLock;
extern GStaticRWLock listaSPLock;

int * loadBalance(int nChunk){

        int i = 0;

        int numeroSP = (int)g_list_length_ts(listaSP, &listaSPLock);

        int * freeSp=calloc(numeroSP,sizeof(int));

        int totalFree = 0;

        struct SUPER_PEER * spt = NULL;

        for(i=0;i<numeroSP;i++){

            spt = (struct SUPER_PEER *)g_list_get_element_at_ts(listaSP,(guint)i, &listaSPLock);

            freeSp[i] = 100 - spt->load;

            totalFree += freeSp[i];

            spt=NULL;

        }

        int newNChunk = 0;

        int newSum=0;

        for(i=0;i<numeroSP;i++){

            newNChunk = (int)((nChunk*freeSp[i])/totalFree);

            freeSp[i] = newNChunk;

            newNChunk = 0;

            newSum += freeSp[i];

        }

        if(newSum>nChunk){
            freeSp[numeroSP-1]--;
        }

        if(newSum<nChunk){
            freeSp[numeroSP-1]++;
        }


    return freeSp;
}
int compareLoad(gconstpointer a, gconstpointer b){
    //ritorna negativo se a è minore di b
    //ritorna zero se sono uguali
    //ritorna maggiore di zero se b è maggiore di a
    struct PEER *pa = (struct PEER *)a;
    struct PEER *pb = (struct PEER *)b;
    if(pa->load<pb->load){
        return -1;
    }
    else if(pa->load==pb->load){
        return 0;
    }
    else if(pa->load>pb->load){
        return 1;
    }
}
struct peerLight * findMin(int nMin){

    struct peerLight * PEERScelti = calloc(nMin,sizeof(struct peerLight));

    struct peerLight *tmp = PEERScelti;

    GList * ricercaPEER = g_list_copy(connectedPeer);

    gint (*compareFunction) (gconstpointer a, gconstpointer b);

    compareFunction=compareLoad;

    ricercaPEER = g_list_sort(ricercaPEER,compareFunction);

    int i = 0;

    struct PEER *tmpPEER = NULL;

    for(i=0;i<nMin;i++){

        tmpPEER = (struct PEER *)g_list_nth_data(connectedPeer,i);

        strcpy(PEERScelti->addr,tmpPEER->addr);

        PEERScelti->port = tmpPEER->port;

        PEERScelti++;
    }

    return tmp;

}
int peerLoadBalance(int nRepliche, struct peerLight * PEERScelti){
    //altri controlli di eventuale presenza di chunk sui PEER?
    int numeroPEER = (int)g_list_length_ts(connectedPeer, &connectedPeerLock);

    if(numeroPEER>=nRepliche){

        //la scelta ricade sui PEER con carico più basso
        PEERScelti = findMin(nRepliche);
        
        //una volta selezionati i PEER si passa al riempimento della lista
        
        return 0;
    }
    else{

        PEERScelti = findMin(numeroPEER);

        return (numeroPEER);
    }

}
